#include "ModuleLoader.h"

namespace modx {
    
    ModuleLoader::ModuleLoader(std::istream& streamModule) {
        char signatureBuffer[5], titleBuffer[23];
        int i = 0;
        unsigned char byte1 = 0;
        ModuleSample tempSample;
        std::istream& is = streamModule;
        
        if(!is) throw(LoaderException("Bad input stream when loading."));
        
        is.seekg(1080); {
            if(!is) throw(LoaderException("Bad input stream when loading."));
        }
        is.read(signatureBuffer, 4); {
            if(!is) throw(LoaderException("Bad input stream when loading."));
        }
        signatureBuffer[4] = '\0';
        moduleSignature = signatureBuffer; {
            if(!moduleSignature.compare("M.K.")) {
                moduleChannels = 4;
            } else if(moduleSignature.find("CHN") != std::string::npos) {
                unsigned char channels = moduleSignature[0]-0x30;
                if(!channels) {
                    throw(LoaderException("Invalid channel signature"));
                }
                moduleChannels = channels;
            } else if(moduleSignature.find("CH") != std::string::npos) {
                unsigned char channels = (moduleSignature[1]-0x30) + (moduleSignature[0]-0x30)*10;
                if(!channels) {
                    throw(LoaderException("Invalid channel signature"));
                }
                moduleChannels = channels;
            } else {
                throw(LoaderException("Invalid channel signature"));
            }
        }
        
        is.seekg(0); {
            if(!is) throw(LoaderException("Bad input stream when loading."));
        }
        is.read(titleBuffer, 20); {
            if(!is) throw(LoaderException("Bad input stream when loading."));
        }
        titleBuffer[20] = '\0';
        moduleTitle = titleBuffer;
        
        for(i = 0; i < 31; i++) {
            is.read(titleBuffer, 22); {
                if(!is) throw(LoaderException("Bad input stream when loading."));
            }    
            titleBuffer[22] = '\0';
            tempSample.sampleName = titleBuffer;
            
            tempSample.sampleLength = getSampleInt(is);
            
            byte1 = (unsigned)is.get();
            if(!is){
                 throw(LoaderException("Bad input stream when loading."));
            }
            tempSample.sampleTuneFrequency = sampleTuneTable[byte1];
            
            tempSample.sampleVolume = (unsigned)is.get();
            if(!is){
                 throw(LoaderException("Bad input stream when loading."));
            }        
                       
            tempSample.sampleLoopStart = getSampleInt(is);
            tempSample.sampleLoopLength = getSampleInt(is);
            moduleSamples.push_back(tempSample);
        } 
		
		songLength = (unsigned)is.get();
		is.get();
        if(!is){
			throw(LoaderException("Bad input stream when loading."));
        }
		
		moduleNumberPatterns = 0;
		for(i = 0; i < 128; i++) {
			moduleOrders.push_back((unsigned)is.get());
			
			if(moduleOrders[i] > moduleNumberPatterns) {
				moduleNumberPatterns = moduleOrders[i];
			}
		} 
		moduleNumberPatterns++;
		moduleOrders.resize(moduleNumberPatterns); 
		
		for(i = 0; i < 4; i++) is.get(); {
			if(!is){
				throw(LoaderException("Bad input stream when loading."));
			}		
		}
		
		unsigned char* rawPattern = NULL;
		unsigned long patternSize = moduleChannels * 256; // channels * 4 * 64
		for(i = 0; i < moduleNumberPatterns; i++) {
			rawPattern = new unsigned char[patternSize];
			
			is.read((char*)rawPattern, patternSize);
			if(!is){
				throw(LoaderException("Bad input stream when loading."));
			}
			
			modulePatterns.push_back(ModulePattern(moduleNotes, moduleChannels, rawPattern));
			delete rawPattern;
		}
		
		ModuleSample *s = NULL;
		for(i = 0; i < 31; i++) {
            s = &moduleSamples[i];
            
            if(s->sampleLength == 0) {
                continue;
            }
            
            try {
                s->sampleData = new char[s->sampleLength];
            } catch(std::bad_alloc e) {
                throw(LoaderException("Bad allocation when loading samples."));
            }
            
 			is.read((char*)s->sampleData, s->sampleLength);
			if(!is){
				throw(LoaderException("Bad input stream when loading."));
			}           
        }
    }
    
    unsigned long ModuleLoader::getSampleInt(std::istream& is) {
        unsigned char byte1 = (unsigned)is.get();
        unsigned char byte2 = (unsigned)is.get();
        
        if(!is){
            throw(LoaderException("Bad input stream when loading."));
        }
        
        unsigned long retval = ((byte1 * 0x100) + byte2) * 2;
             
        if(retval <= 4) {
            retval = 0;
        }   
        
        return retval;
    }
}
